package graphics;

import graphics.UsefulFunctions;
import org.javia.arity.Function;
import graphics.UsefulFunctions.Intrpl;
import graphics.UsefulFunctions.my_Vector3d;

public class RendererNormal extends AbsRenderer {

	Function func;
	Function funx;
	Function funz;

	public RendererNormal(int height, int width, Function f, Function dx,
			Function dz) {
		func = f;
		funx = dx;
		funz = dz;
	}

	// ==============================================
	float Intensity(float ldotn, float z, StatesCapsule sC)
	// Return intensity based on normal and distance
	// ==============================================
	{
		float intens;
		float denom = z / sC.getRADIUS() + sC.getDist();
		if (ldotn < sC.getThldn())
			ldotn = sC.getThldn();
		if (denom < 1.e-7)
			intens = 1.0f;
		else {
			intens = sC.getAMBIENT() + sC.getIp() * ldotn / denom;
			if (intens > 1.0)
				intens = 1.0f;
		}
		return (intens);
	} // end of Intensity

	public void Putpixel(int x, int y, int base, float intens) {
		if (x < 0 || y < 0 || y >= tmp_height )
			return;
		int r, g, b;
		g = b = (int) (intens * 255);
		r = (base == 64) ? g : 0;
		try {
			screen[x][y] = (255 << 24) | (r << 16) | (g << 8) | b;
		} catch (Exception e) {
			//System.out.print('e');
		}
	}

	public void render(int height, int width, StatesCapsule sC)
	// Floating horizon with normal interpolation shading
	// ===================================================================
	{
		Intrpl Intrpl_x, Intrpl_z, Intrpl_Z; // structures for interpolation
		Intrpl_x = new Intrpl();
		Intrpl_Z = new Intrpl();
		Intrpl_z = new Intrpl();
		tmp_height = height;
		int ymin; // floating horizon current minimum
		int ymax; // floating horizon current maximum
		int yprev; // previous y
		float z, X, Y, Z, xtag, ytag, ztag;
		float Xcb, Xsb;
		float dfdu, dfdv, len, LdotN;
		float YSfl;
		// float Nz; //a, b, c, d, t, sq, tand, tang,
		float LdotN_old, z_old; // , Nz_old, y_old;
		float Zt, Z_old, z_save;
		float dfdu_old, dfdv_old;// , xt, zt;
		float dfx, dfz;// , delx, delz, dfdur, dfdvr;
		float LdN, ln;
		int XS, YS, j, firstXS, lastXS, base_col;
		screen = new int[height + 1][width + 1];
		// System.out.println("Normal: " + height + "," + width);
		z_old = dfdu_old = dfdv_old = Z_old = 0.0f;
		firstXS = sC.getMidx()
				- (int) (sC.getRADIUS() * sC.getScreen_to_max_crd_ratio());
		lastXS = sC.getMidx()
				+ (int) (sC.getRADIUS() * sC.getScreen_to_max_crd_ratio());

		for (XS = firstXS; XS <= lastXS; XS++) {

			LdotN_old = 0.0f;
			yprev = 99999; // Mark for first pixel in column.
			ymin = 99999; // Initialize min value in column.
			ymax = -99999; // Initialize max value in column.
			X = (XS - sC.getMidx()) / sC.getScreen_to_max_crd_ratio();
			Xcb = X * sC.getCos_beta();
			Xsb = X * sC.getSin_beta();
			z = sC.getNeg_t_plane_limit();
			z_save = 99999.0f;
			while (z <= sC.getPos_t_plane_limit()) {
				xtag = Xcb + z * sC.getSin_beta();
				ztag = -Xsb + z * sC.getCos_beta();
				dfdu = (float) funx.eval(xtag, ztag); // partial x-derivative
				dfdv = (float) funz.eval(xtag, ztag); // partial z-derivative
				len = (float) Math.sqrt(dfdu * dfdu + 1.0 + dfdv * dfdv); // length
																			// of
																			// L.N
				LdotN = (dfdu * sC.getCosa_x_sinb() - sC.getSin_alpha() + dfdv
						* sC.getCosa_x_cosb())
						/ len;
				ytag = (float) func.eval(xtag, ztag); // calculate function
				// Nz = -dfdu*sC.getSin_beta() - dfdv*sC.getCos_beta();
				if (sC.getRidge() && z_save == 99999. && yprev != 99999
						&& LdotN * LdotN_old < 0.0) { // begin supersampling
														// procedure:
					z_save = z;
					z = z_old + 0.0625f * sC.getT_STEP();
					continue;
				}

				// continue regular treatment:
				Y = ytag * sC.getCos_alpha() - z * sC.getSin_alpha();
				Z = ytag * sC.getSin_alpha() + z * sC.getCos_alpha();
				YSfl = Y * sC.getScreen_to_max_crd_ratio() + sC.getMidy();
				YS = (int) UsefulFunctions.my_Round(YSfl);
				if (yprev == 99999) // first pixel drawn in column?
				{
					base_col = (LdotN >= 0.0) ? 64 : 128;
					ymin = ymax = YS;
					Putpixel(XS, YS, base_col, Intensity((float) Math
							.abs(LdotN), Z, sC));
				}
				if (YS < ymin) // draw lower surface:
				{
					Intrpl_x.init((float) yprev, dfdu_old, (float) YS, dfdu);
					Intrpl_z.init((float) yprev, dfdv_old, (float) YS, dfdv);
					Intrpl_Z.init((float) yprev, Z_old, (float) YS, Z);
					for (j = ymin - 1; j >= YS; j--) {
						dfx = Intrpl_x.InterValue((float) j);
						dfz = Intrpl_z.InterValue((float) j);
						Zt = Intrpl_Z.InterValue((float) j);
						ln = (float) Math.sqrt(dfx * dfx + 1. + dfz * dfz);
						LdN = (dfx * sC.getCos_beta() - sC.getSin_alpha() + dfz
								* sC.getCosa_x_cosb())
								/ ln;
						Putpixel(XS, j, 128, Intensity(-LdN, Zt, sC));
					}
					ymin = YS;
				}
				if (YS > ymax) // draw upper surface:
				{
					Intrpl_x.init((float) yprev, dfdu_old, (float) YS, dfdu);
					Intrpl_z.init((float) yprev, dfdv_old, (float) YS, dfdv);
					Intrpl_Z.init((float) yprev, Z_old, (float) YS, Z);
					for (j = ymax + 1; j <= YS; j++) {
						dfx = Intrpl_x.InterValue((float) j);
						dfz = Intrpl_z.InterValue((float) j);
						Zt = Intrpl_Z.InterValue((float) j);
						ln = (float) Math.sqrt(dfx * dfx + 1. + dfz * dfz);
						LdN = (dfx * sC.getCosa_x_sinb() - sC.getSin_alpha() + dfz
								* sC.getCosa_x_cosb())
								/ ln;
						Putpixel(XS, j, 64, Intensity(LdN, Zt, sC));
					}
					ymax = YS;
				}
				yprev = YS;
				Z_old = Z;
				LdotN_old = LdotN;
				z_old = z;
				// Nz_old = Nz; y_old = ytag;
				dfdu_old = dfdu;
				dfdv_old = dfdv;
				if (z_save != 99999.) {
					z += 0.0625 * sC.getT_STEP();
					if (z >= z_save) {
						z = z_save;
						z_save = 99999.0f;
					}
				} else
					z += sC.getT_STEP();
			} // of z axis for

		} // of x axis for
	}

}
